#include <stdio.h>
#include "dict.h"

void CDict::freeSubTree(CDict::TState &root)
{
  // free up the trie recurrently.
  if (root.follow != NULL) {
    Map_Type &map = *(root.follow);
    for (Map_Type::iterator it = map.begin(), last = map.end();
         it != last; ++it) 
      freeSubTree(it->second);
    delete root.follow;
    root.follow = NULL;
  }
}

const CDict::TState *CDict::step(const CDict::TState *root, TWCHAR wch) {
  if ((root != NULL) && (root->follow != NULL) && wch != WCH_NULL) {
    Map_Type::iterator it = root->follow->find(TChar(wch));
    if (it != root->follow->end())
      return &(it->second);
  }
  return NULL;
}

void CDict::insertWord(const TWCHAR *wstr, TWORDID id) {
  TState *ps = &m_root;         // the root node
  while (*wstr) {
    TChar ch(*wstr++);          // is it a bug?
    TWORDID nodeId = (*wstr)?ID_NOT_WORD:id;
    if (ps->follow == NULL) {
      ps->follow = new Map_Type(); // the next layer of nodes is empty.
    }

    Map_Type &map = *(ps->follow); // point to the next layer
    Map_Type::iterator it = map.find(ch); // find the key at current layer
    if (it != map.end() && it->second.word_id != ID_NOT_WORD &&
        it->second.word_id != nodeId && nodeId != ID_NOT_WORD) {
      throw new int(100);       // the same word.but with different id
    }
    if (it != map.end()) {      // found previous pushed key
      if (nodeId != ID_NOT_WORD)
				it->second.word_id = nodeId;
      ps = &(it->second);     // ps goto next layer
    } else {
      ps = &(map[ch] = TState(nodeId)); // Cannot find the keyValue, so
    }                                   // make a new one!
  }
}

bool
CDict::parseText(const char* filename)
{
  FILE * fp = NULL;
  static char buf[1024];
  static TWCHAR wword[sizeof(buf)];
  unsigned int id;

  try {
      if ((fp = fopen(filename, "r")) == NULL)
        return false;
      while (fgets(buf, 1024, fp) != NULL) {
          if (*buf == '\n' || *buf == '#')
              continue;

          char* p = buf;
          while (*p == ' ' || *p == '\t')
              ++p;
          char* pstart = p;
          while (*p != 0 && *p != ' ' && *p != '\t')
              ++p;
          if (*p == 0)
              continue;
          *p++ = 0;
          while (*p == ' ' || *p == '\t')
              ++p;
          if (!(*p >= '0' && *p <= '9')) continue;
          for (id=0; *p >= '0' && *p <= '9'; ++p)
              id = 10*id + (*p - '0');

          if (id < ID_REALWORD_START)
              continue;
          if (MBSTOWCS(wword, pstart, sizeof(buf)) != (size_t)-1) {
              insertWord(wword, TWORDID(id));
          } else {
              fprintf(stderr, "mbs to wcs conversion error for : %s %d\n", buf, id);
              exit(100);
          }
      }
			//PrintOut(fp);
      fclose(fp);
  } catch (...) {
      if (fp != NULL)
          fclose(fp);
      buf[sizeof(buf)-1] = 0;
      fprintf(stderr, "Catch exception when loading dictionary at %s, existing...", buf);
      exit(200);
  }
  return true;
}

void CDict::InnerPrint(FILE *fp, wstring &wstr, const TState *pnode)
{
  if (pnode != NULL && pnode->word_id != ID_NOT_WORD) {
    char *buf = new char[wstr.size()*2 + 2];
    WCSTOMBS(buf, wstr.c_str(), wstr.size()*2 + 2);
    fprintf(fp, "%s %d\n", buf, unsigned(pnode->word_id));
    delete [] buf;
  }
  if (pnode != NULL && pnode->follow != NULL) {
    Map_Type::iterator it, ite = pnode->follow->end();
    for (it = pnode->follow->begin(); it != ite; ++it) {
      TWCHAR wch = TWCHAR(it->first);
      wstr.push_back(wch);
      InnerPrint(fp, wstr, &(it->second));
      wstr.erase(wstr.size() - 1, 1);
    }
  }
}

// FMM
int CDict::matchLongest(const CDict::TState *root, CDict::PState &result,
                        const TWCHAR *str) {
  int lastWordLen = 0, len = 0;
  result = root;
  while (root != NULL) {
    if (root->word_id != ID_NOT_WORD) {
      result = root;
      lastWordLen = len;
    }
    ++len;
    root = step(root, *str++);
  }
  return lastWordLen;
}

#ifdef MTEST 
int main() {
	FILE *fplog = fopen("log", "w");
	if (!fplog) 
		return false;
  CDict *dt = new CDict();
  if (!dt->parseText("dict.utf8")) {
    fprintf(stderr, "fail to open lexicon file\n");
    fflush(stderr);
    exit(11);
  }
  dt->PrintOut(fplog);
  delete dt;
  dt = NULL;
	fclose(fplog);
  return 0;
}
#endif
